#include <QDebug>
#include <QPainter>
#include "player.h"
#include "losttankwindow.h"
#include "ca-pathfinding/CAs/TargetFindingCA.h"
#include "ca-pathfinding/CAs/PathSmoothingCA.h"
#include "arena.h"



Player::Player():Object()
{
    type=ot::PLAYER;
    texture=QPixmap(":/resources/tank.png");
    angle=0;
    speed=0;
    rot=0;
    friction=0.9925;
    rotfriction=0.91;
    acc=0.075;
    brake=0.15;
    rotacc=0.25;
    shield=5;
    health=100;
    alive=true;
    firing_range=350;
    path=std::shared_ptr<std::list<std::pair<int,Tile*> > >(new std::list<std::pair<int,Tile*> >());
}

void Player::init()
{
    current_states=grid->copy();
    new_states=grid;
}


void Player::purge()
{
    alive=false;
    texture=QPixmap(":/resources/tank_broken.png");
}


void Player::fire()
{

    static ObjectFactory f(purgatory);

    Bullet* b=f.createBullet(this);
    if(!b->scene())arena->addBullet(b);
    b->resetTransform();
    b->setPos(scenePos());
    b->rotate(angle);
    LostTankWindow::pressedFire=false;



}
void Player::hit(int val)
{
    if(shield>0)--shield;
    else
    {
        health-=val;
        if(health<=0)purge();
    }
}

void Player::control()
{

    if(LostTankWindow::pressedFire)
    {
        fire();
    }

    if(LostTankWindow::pressedAutoObjective)
    {
        LostTankWindow::pressedAutoObjective=false;
        if(grid->objects[ot::OBJECTIVE].empty())return;
        grid->objects[ot::OBJECTIVE].push_back(grid->objects[ot::OBJECTIVE].front());
        grid->objects[ot::OBJECTIVE].erase(grid->objects[ot::OBJECTIVE].begin());
        speed=0;
        rot=0;
        Object* o=grid->objects[ot::OBJECTIVE].front();
        int i=o->tile->y/Tile::TILE_SIZE;
        int j=o->tile->x/Tile::TILE_SIZE;
        auto nbs=mn::neighbours(grid,i,j);

        // usuwam sasiadow ze stanem WALL
        nbs.erase(std::remove_if(nbs.begin(), nbs.end(),
                                 [](Tile* t)->bool
        {
                                 return t->state==WALL;
    }), nbs.end());
        if(nbs.empty())return;

        Objective obj;
        obj.tile=nbs[0];
        std::shared_ptr<std::list<std::pair<int,Tile*> > > p (findPath(&obj));
        std::shared_ptr<std::list<std::pair<int,Tile*> > > tmp;

        for(auto t:nbs)
        {
            Objective a;
            a.tile=t;
            tmp=std::shared_ptr<std::list<std::pair<int,Tile*> > >(findPath(&a));
            if(tmp->size()<p->size()) p=tmp;
        }
        path=p;
        path->pop_back();
        return;
    }

if(LostTankWindow::pressedAutoPackage)
{

    LostTankWindow::pressedAutoPackage=false;
    if(grid->objects[ot::PACKAGE].empty())return;
    grid->objects[ot::PACKAGE].push_back(grid->objects[ot::PACKAGE].front());
    grid->objects[ot::PACKAGE].erase(grid->objects[ot::PACKAGE].begin());
    speed=0;
    rot=0;
    path=findPath(grid->objects[ot::PACKAGE].front());
    return;
}

if(LostTankWindow::pressedLead)
{

    if(path->empty())
    {
        LostTankWindow::pressedLead=false;
        speed=0;
        return;
    }

    float alpha=path->front().first;
    if(angle==alpha)
    {
        speed+=acc;
        rot=0.0f;
    }
    else
    {

        speed=0.0f;

        int th1=angle+360-alpha;

        if(th1<0)th1+=360;
        else if(th1>359)th1-=360;

        int th2=360-th1;

        if(th2<0)th2+=360;
        else if(th2>359)th2-=360;

        if(fabs(alpha-angle)<0.5f)rot=0.0f;
        else if(th1>th2) rot-=rotacc;
        else rot+=rotacc;

    }
    return;
}


if(LostTankWindow::pressedForward)speed+=acc;
if(LostTankWindow::pressedBack)speed-=acc;
if(LostTankWindow::pressedBrake)
{

    if(speed>1) speed-=brake;
    else if(speed<-1) speed+=brake;
    else speed=rot=0;
}
if(LostTankWindow::pressedRotateLeft)rot+=rotacc;
if(LostTankWindow::pressedRotateRight)rot -=rotacc;

}

void Player::physics()
{

    if(fabs(speed)<acc*0.25)speed=0;
    else speed*=friction;
    if(fabs(rot)<rotacc)rot=0;
    else rot*=rotfriction;


}

bool Player::isColliding()
{
    float buff=8;



    for(Object* o:grid->objects[ot::WALL])
    {
        Wall* w=dynamic_cast<Wall*>(o);
        float d=distance(scenePos().x(),scenePos().y(),w->scenePos().x(),w->scenePos().y());
        if(d<boundingRect().width())
        {
            speed=0;
            rot=0;
            return true;
        }
    }
    for(Object* o:grid->objects[ot::OBJECTIVE])
    {
        Objective* w=dynamic_cast<Objective*>(o);
        float d=distance(scenePos().x(),scenePos().y(),w->scenePos().x(),w->scenePos().y());
        if(d<boundingRect().width())
        {
            speed=0;
            rot=0;
            return true;
        }
    }
    auto i = std::begin(grid->objects[ot::PACKAGE]);
    for(Object* o:grid->objects[ot::PACKAGE])
    {
        Package* p=dynamic_cast<Package*>(o);
        float d=distance(scenePos().x(),scenePos().y(),p->scenePos().x(),p->scenePos().y());
        if(d<boundingRect().height()/2+p->boundingRect().height()/2+buff)
        {
            grid->objects[ot::PACKAGE].erase(i);
            if (p->ptype==pt::HEALTH)
                health+=p->value;
            else if(p->ptype==pt::SHIELD)
                shield+=p->value;
            p->purge();

            return false;
        }
        ++i;
    }

    return false;
}

std::shared_ptr<std::list<std::pair<int,Tile*> > > Player::findPath(Object * o)
{
    auto path=std::shared_ptr<std::list<std::pair<int,Tile*> > >(new std::list<std::pair<int,Tile*> >);
    current_states->refresh();
    new_states->refresh();
    TargetFindingCA t;
    t.current_states=current_states;
    t.new_states=new_states;
    current_states->start=tile;
    current_states->stop=o->tile;
    new_states->start=tile;
    new_states->stop=o->tile;
    t.start();
    PathSmoothingCA p;
    p.current_states=current_states;
    p.new_states=new_states;
    p.start();

    Tile* next=tile;


    while(next->state==EMPTY)
    {
        int i=next->y/Tile::TILE_SIZE;
        int j=next->x/Tile::TILE_SIZE;
        next->state=MARKED;
        auto ns=mn::neighbours(grid,i,j);

        if(ns[0]->state==EMPTY)
        {
            next=ns[0];
            path->push_back(std::pair<int,Tile*>(0,ns[0]));

        }
        else if(ns[2]->state==EMPTY)
        {
            next=ns[2];
            path->push_back(std::pair<int,Tile*>(90,ns[2]));
        }
        else if(ns[4]->state==EMPTY)
        {
            next=ns[4];
            path->push_back(std::pair<int,Tile*>(180,ns[4]));
        }
        else if(ns[6]->state==EMPTY)
        {
            next=ns[6];
            path->push_back(std::pair<int,Tile*>(270,ns[6]));

        }
        else if(ns[1]->state==EMPTY)
        {
            next=ns[1];
            path->push_back(std::pair<int,Tile*>(45,ns[1]));
        }
        else if(ns[3]->state==EMPTY)
        {
            next=ns[3];
            path->push_back(std::pair<int,Tile*>(135,ns[3]));
        }
        else if(ns[5]->state==EMPTY)
        {
            next=ns[5];
            path->push_back(std::pair<int,Tile*>(225,ns[5]));
        }
        else if(ns[7]->state==EMPTY)
        {
            next=ns[7];
            path->push_back(std::pair<int,Tile*>(315,ns[7]));
        }

    }
    if(path->empty())return path;
    int tx=path->front().second->x-grid->width()*Tile::TILE_SIZE/2+Tile::TILE_SIZE/2;
    int ty=path->front().second->y-grid->height()*Tile::TILE_SIZE/2+Tile::TILE_SIZE/2;
    int px=scenePos().x();
    int py=scenePos().y();
    float d=distance(px,py,tx,ty);

    int alpha=0;
    if(d>0)
    {
        if(px>tx && py<=ty)
            alpha=180+180*asin(distance(tx,py,px,py)/d)/M_PI;
        else if(px>tx && py>ty)
            alpha=270+180*asin(distance(px,ty,px,py)/d)/M_PI;
        else if(px<=tx && py>ty)
            alpha=180*asin(distance(tx,py,px,py)/d)/M_PI;
        else alpha=90+180*asin(distance(px,ty,px,py)/d)/M_PI;

        if(alpha==360)alpha=0;
        if(alpha<0)alpha+=360;
        else if(alpha>359)alpha-=360;

        Tile* t=path->front().second;
        alpha=round(alpha);
        path->pop_front();
        path->push_front(std::pair<int,Tile*>(alpha,t));
    }

    return path;

}

void Player::step()
{
    bool rep=(rot==0 && angle!=int(angle));
    if(rep)
    {
        float x=angle-int(angle);
        if(x>0.5f)rot=x+1;
        else rot=x;
    }
    rotate(-rot);
    translate(0,-speed);

    if (isColliding())return;

    angle-=rot;
    if(rep)rot=0;
    if(angle<0) angle+=360;
    else if(angle>360) angle-=360;

    std::vector<Tile*>nbs=mn::neighbours(grid,tile->y/Tile::TILE_SIZE,tile->x/Tile::TILE_SIZE);
    float d,d1,d2,tx,ty,nx,ny;
    int eps=Tile::TILE_SIZE/5;
    for( auto* n:nbs)
    {
        tx=tile->x-grid->width()*Tile::TILE_SIZE/2+Tile::TILE_SIZE/2;
        ty=tile->y-grid->height()*Tile::TILE_SIZE/2+Tile::TILE_SIZE/2;
        nx=n->x-grid->width()*Tile::TILE_SIZE/2+Tile::TILE_SIZE/2;
        ny=n->y-grid->height()*Tile::TILE_SIZE/2+Tile::TILE_SIZE/2;
        d1=distance(scenePos().x(),scenePos().y(),tx,ty);
        d2=distance(scenePos().x(),scenePos().y(),nx,ny);
        if(d1>d2 && abs(d1-d2)>eps) tile=n;

    }
    tx=tile->x-grid->width()*Tile::TILE_SIZE/2+Tile::TILE_SIZE/2;
    ty=tile->y-grid->height()*Tile::TILE_SIZE/2+Tile::TILE_SIZE/2;
    d=distance(scenePos().x(),scenePos().y(),tx,ty);
    if(!path->empty() && tile==path->front().second && d<eps/1.5)path->pop_front();

}

void Player::paint(QPainter *painter, const QStyleOptionGraphicsItem *,QWidget *)
{
    painter->drawPixmap(boundingRect().toRect(),texture);
}

QRectF Player::boundingRect() const
{
    const static QRectF r(-texture.width()/2,-texture.height()/2,texture.width(),texture.height());
    return r;

}
